NICE 본인인증 클라이언트 통합 가이드

나이스페이 본인인증 서비스를 프론트엔드에 통합하기 위한 핵심 가이드

📋 개요

사용자 클릭 → 본인인증 팝업 → 인증 완료 → 사용자 정보 수신의 플로우를 구현합니다. 백엔드는 암호화/복호화를 담당하고, 프론트는 팝업 관리와 데이터 전달을 담당합니다.


🔄 인증 플로우 (3단계)

1. POST /api/nice/auth/request
   → 암호화된 인증 데이터 받기 (tokenVersionId, encData, integrityValue)

2. form POST → NICE 팝업
   → 사용자 본인인증 진행

3. 콜백 → postMessage → POST /api/nice/auth/callback
   → 복호화된 사용자 정보 수신 (이름, 생년월일, 휴대폰, CI/DI 등)

🔌 백엔드 API

1. 본인인증 요청

POST /api/nice/auth/request
Content-Type: application/json

{
  "requestno": "REQ1234567890",
  "authtype": "M",  // M: 휴대폰, C: 카드, X: 아이핀
  "returnurl": "https://example.com/niceauth-callback"
}

Response:
{
  "success": true,
  "data": {
    "tokenVersionId": "20250113...",
    "encData": "BASE64_ENCRYPTED_DATA",
    "integrityValue": "HMAC_SHA256_VALUE"
  }
}

2. 인증 결과 복호화

POST /api/nice/auth/callback
Content-Type: application/json

{
  "enc_data": "RETURN_ENC_DATA",
  "integrity_value": "RETURN_INTEGRITY_VALUE",
  "token_version_id": "20250113..."
}

Response:
{
  "success": true,
  "data": {
    "dataBody": {
      "name": "홍길동",
      "birthdate": "19900101",
      "gender": "1",
      "mobileno": "01012345678",
      "mobileco": "SKT",
      "nationalinfo": "0",
      "ci": "CI_BASE64_STRING",
      "di": "DI_BASE64_STRING"
    }
  }
}

💻 클라이언트 구현

1️⃣ 본인인증 요청 및 팝업 열기

async function requestNiceAuth(authType: 'M' | 'C' | 'X') {
  // 1) 백엔드에 암호화 데이터 요청
  const res = await fetch('/api/nice/auth/request', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      requestno: `REQ${Date.now()}`,
      authtype: authType,
      returnurl: `${location.origin}/niceauth-callback`
    })
  });
  
  const json = await res.json();
  if (!json.success) throw new Error(json.message);
  
  const { tokenVersionId, encData, integrityValue } = json.data;
  
  // 2) NICE 팝업 열기 (form POST 방식)
  const form = document.createElement('form');
  form.method = 'POST';
  form.action = 'https://nice.checkplus.co.kr/CheckPlusSafeModel/checkplus.cb';
  form.target = 'nice_popup';
  
  const fields = {
    m: 'service',
    token_version_id: tokenVersionId,
    enc_data: encData,
    integrity_value: integrityValue
  };
  
  Object.entries(fields).forEach(([k, v]) => {
    const input = document.createElement('input');
    input.type = 'hidden';
    input.name = k;
    input.value = v as string;
    form.appendChild(input);
  });
  
  document.body.appendChild(form);
  const popup = window.open('', 'nice_popup', 'width=500,height=600');
  
  if (!popup) {
    alert('팝업 차단됨 - 브라우저 팝업 허용 필요');
    return;
  }
  
  form.submit();
  document.body.removeChild(form);
}

2️⃣ 부모창 - postMessage 수신 및 복호화 요청

useEffect(() => {
  const handleMessage = async (event: MessageEvent) => {
    // 보안: origin 체크
    if (event.origin !== window.location.origin) return;
    
    if (event.data?.type === 'NICE_AUTH_RESULT') {
      const { enc_data, integrity_value, token_version_id } = event.data.data;
      
      // 백엔드에 복호화 요청
      const res = await fetch('/api/nice/auth/callback', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ enc_data, integrity_value, token_version_id })
      });
      
      const result = await res.json();
      if (result.success) {
        // 사용자 정보 사용 (result.data.dataBody)
        console.log('인증 성공:', result.data.dataBody);
      }
    }
  };
  
  window.addEventListener('message', handleMessage);
  return () => window.removeEventListener('message', handleMessage);
}, []);

3️⃣ 콜백 페이지 (퍼블릭 라우트)

파일 위치: /app/niceauth-callback/page.tsx (인증 미적용)

'use client';

import { useEffect } from 'react';
import { useSearchParams } from 'next/navigation';

export default function NiceAuthCallbackPage() {
  const params = useSearchParams();
  
  useEffect(() => {
    const enc_data = params.get('enc_data');
    const integrity_value = params.get('integrity_value');
    const token_version_id = params.get('token_version_id');
    
    if (window.opener && !window.opener.closed) {
      window.opener.postMessage(
        {
          type: 'NICE_AUTH_RESULT',
          data: { enc_data, integrity_value, token_version_id }
        },
        window.location.origin
      );
    }
    
    setTimeout(() => window.close(), 500);
  }, [params]);
  
  return (
    <div className="p-8 text-center">
      <p>인증 처리 중...</p>
      <p className="text-sm text-gray-500">창이 자동으로 닫힙니다.</p>
    </div>
  );
}

⚠️ 주의사항

필수 체크포인트

Next.js 빌드 이슈

// ❌ 빌드 실패 (SSR 시 useSearchParams 에러)
export default function Page() {
  const params = useSearchParams(); // prerender 에러
}

// ✅ 해결
'use client'; // 클라이언트 컴포넌트로 명시
export default function Page() {
  const params = useSearchParams();
}

보안


✅ 테스트 체크리스트

개발 환경

스테이징


📁 파일 구조 예시

your-project/
├── app/
│   ├── (dashboard)/
│   │   └── signup/
│   │       └── page.tsx          # 본인인증 요청 버튼 위치
│   └── niceauth-callback/
│       └── page.tsx               # 콜백 (퍼블릭)
└── lib/
    └── utils/
        └── niceAuth.ts            # 유틸 함수

🚀 빠른 시작

  1. 콜백 페이지 생성 (/app/niceauth-callback/page.tsx)
  2. 본인인증 버튼에 requestNiceAuth() 연결
  3. postMessage 리스너 등록
  4. 스테이징 환경에서 테스트
  5. 운영 배포

📞 문의

백엔드 API 관련 문의: [백엔드팀]
프론트 구현 참고: /apps/akc-b2c-admin/src/app/(dashboard)/niceauth/